package com.ludomancy.gamelib;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class GameCanvas extends Canvas implements MouseListener, MouseMotionListener, KeyListener, Runnable
{
	private Image backBuffer;
    private Graphics backBufferGC;
    private Widget root; // top level widget
    public static GameCanvas screen;
    private Widget modalWidget = null;
    private Widget mouseCapturing = null;
    private Widget lastHoveringWidget = null; // the last widget the cursor was over
    public Theme theme;

    public GameCanvas(Theme theme)
    {
        screen = this;
        addMouseListener(this);
        addMouseMotionListener(this);
        addKeyListener(this);
        this.theme = theme;
    }
    
    public int getWidth()
    {
    	return getBounds().width;
    }
    
    public int getHeight()
    {
    	return getBounds().height;
    }
    
    public void showWidgetModal(Widget widget)
    {
    	modalWidget = widget;
    	modalWidget.onShow();
    }
    
    public void showWidget(Widget widget)
    {
    	root = widget;
    	root.onShow();
    }
    
    public Widget getModalWidget()
    {
    	return modalWidget;
    }
    
    public boolean isRoot(Widget widget)
    {
    	return widget == root;
    }
    
    public void run()
    {
    	if(root != null)
    	{
    		root.managerRun();
    	}
        repaint();
    }
    
    protected void finalize() throws Throwable
    {
        try
        {
            backBufferGC.dispose();
        }
        finally
        {
            super.finalize();
        }
    }

	public void mouseClicked(MouseEvent arg0)
	{
	}

	public void mouseEntered(MouseEvent arg0)
	{
	}

	public void mouseExited(MouseEvent arg0)
	{
	}
	
	void mouseCapture(Widget capturing)
	{
		mouseCapturing = capturing;
	}
	
	void mouseRelease(Widget from)
	{
		if(from == mouseCapturing)
		{
			mouseCapturing = null;
		}
	}

	public void mousePressed(MouseEvent arg0)
	{
		lastCursorPosition = new Point(arg0.getX(), arg0.getY());
		if(mouseCapturing != null)
		{
			mouseCapturing.mouseDown(mouseCapturing.screenToWidget(new Point(arg0.getX(), arg0.getY())));
		}
		else
		if(modalWidget != null)
		{
			modalWidget.managerMouseDown(new Point(arg0.getX(), arg0.getY()));
		}
		else
		if(root != null)
		{
			root.managerMouseDown(new Point(arg0.getX(), arg0.getY()));
		}
	}

	public void mouseReleased(MouseEvent arg0)
	{
		lastCursorPosition = new Point(arg0.getX(), arg0.getY());
		if(mouseCapturing != null)
		{
			mouseCapturing.mouseUp(mouseCapturing.screenToWidget(new Point(arg0.getX(), arg0.getY())));
		}
		else
		if(modalWidget != null)
		{
			modalWidget.managerMouseUp(new Point(arg0.getX(), arg0.getY()));
		}
		else
		if(root != null)
		{
			if(draggingWidget != null)
			{
				Widget under = Widget.getWidgetUnder(root, new Point(arg0.getX(), arg0.getY()), true);
				draggingTarget = under;
				if(under != null)
				{
					under.onDrop(draggingWidget, under.screenToWidget(new Point(arg0.getX(), arg0.getY())), dragObject);
				}
				draggingWidget = null;
				dragObject = null;
			}
			else
			{
				root.managerMouseUp(new Point(arg0.getX(), arg0.getY()));
			}
		}
	}
	
	private Widget draggingWidget = null;
	private Widget draggingTarget = null;
	Object dragObject = null;
	private Point lastCursorPosition = null;
	
	public void forceDragStart(Widget under, Point cursor)
	{
		if(under != null)
		{
			if(draggingWidget == null)
			{
				draggingWidget = under;
				dragObject = draggingWidget.beginDrag(draggingWidget.screenToWidget(lastCursorPosition));
				if( dragObject != null)
				{
					draggingWidget = under;
					draggingTarget = under;
				}
			}
		}
	}
	
	public void mouseDragged(MouseEvent arg0)
	{
		lastCursorPosition = new Point(arg0.getX(), arg0.getY());
		if(root != null)
		{
			Widget under = Widget.getWidgetUnder(root, new Point(arg0.getX(), arg0.getY()), true);
			if(under != null)
			{
				if(draggingWidget == null)
				{
					forceDragStart(under, new Point(arg0.getX(), arg0.getY()));
				}
				else
				{
					if(dragObject != null)
					{
						draggingTarget = under;
						draggingTarget.onDrag(draggingTarget.screenToWidget(new Point(arg0.getX(), arg0.getY())), dragObject);
					}
				}
			}
		}
	}
	
	public void paint(Graphics g)
	{
        if(backBuffer == null)
        {
            backBuffer = createImage(getSize().width, getSize().height);
            backBufferGC = backBuffer.getGraphics();
            backBufferGC.setColor(Color.RED);
            backBufferGC.fillRect(0,0,backBuffer.getWidth(null),backBuffer.getHeight(null));
        }
        Graphics screenGraphics = g;
        g = backBufferGC;
        
        // setup a good rendering quality
		Graphics2D g2 = (Graphics2D) g;
		
		RenderingHints renderHints = new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		renderHints.put(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		g2.addRenderingHints(renderHints);
		
		g2.setFont(screen.theme.getDefaultFont());
		
		if(root != null)
		{
			root.managerPaint(g);
		}
		if(modalWidget != null)
		{
			modalWidget.managerPaint(g);
		}

		// paint cursor
		if(draggingWidget != null && draggingTarget != null && lastCursorPosition != null)
		{
			// target first chance
			if(!draggingTarget.paintDragCursor(g, lastCursorPosition, dragObject, true))
			{
				//...then leave it to the issuer
				draggingWidget.paintDragCursor(g, lastCursorPosition, dragObject, true);
			}
				
		}
        screenGraphics.drawImage(backBuffer, 0, 0, null);
	}
	
	public void update(Graphics g)
	{
		paint(g);
	}
		
    public Image loadImage(String file)
    {
        Toolkit t = getToolkit();
        Image img = t.getImage(file);
        MediaTracker m = new MediaTracker(this);
        m.addImage( img, 1 );
        try
        {
        	m.waitForAll();
        }
        catch( InterruptedException e )
        {
        }
        return img;
    }
    

	public void mouseMoved(MouseEvent arg0)
	{
		lastCursorPosition = new Point(arg0.getX(), arg0.getY());
		if(mouseCapturing != null)
		{
			mouseCapturing.mouseMoved(mouseCapturing.screenToWidget(new Point(arg0.getX(), arg0.getY())));
		}
		else
		if(modalWidget != null)
		{
			modalWidget.mouseMoved(new Point(arg0.getX(), arg0.getY()));
		}
		else
		if(root != null)
		{
			if(root.managerMouseMove(new Point(arg0.getX(), arg0.getY())) != null)
			{
            	Widget w = Widget.getWidgetUnder(root, new Point(arg0.getX(), arg0.getY()), true);
            	if(lastHoveringWidget != w)
            	{
            		if(lastHoveringWidget != null)
            		{
            			lastHoveringWidget.onMouseLeave();
            		}
            		lastHoveringWidget = w;
            		w.onMouseEnter();
            	}
			}
		}

	}
	
	public void destroyWidget(Widget widget)
	{
		if(widget == null)
		{
			return;
		}
    	if(widget.parent != null)
    	{
    		widget.setParent(null);
    	}
    	if(widget == root)
    	{
    		root = null;
    	}
    	if(widget == draggingWidget)
    	{
    		draggingWidget = null;
    	}
    	if(widget == draggingTarget)
    	{
    		draggingTarget = null;
    	}
    	if(modalWidget == widget)
    	{
    		modalWidget = null;
    	}
    	if(mouseCapturing == widget)
    	{
    		mouseRelease(widget);
    	}
    	// free up children
        for (int i = 0; i < widget.children.size(); i++)
        {
            Widget w = (Widget) widget.children.elementAt(i);
            destroyWidget(w);
        }
	}

	public void keyPressed(KeyEvent e)
	{
		if(root != null)
		{
			root.keyPressed(e);
		}
	}

	public void keyReleased(KeyEvent e)
	{
		if(root != null)
		{
			root.keyReleased(e);
		}
	}

	public void keyTyped(KeyEvent e)
	{
		if(root != null)
		{
			root.keyTyped(e);
		}
	}
}
